﻿Public Class RegistryEditor

    Public Function GetNode(ByVal Root As String) As String
        Dim jPattern = "{{""text"":""{0}"",""id"":""{1}\/{0}""}},{2}"
        Dim jRegistry As String = ""
        If Root = "Registry" Then
            jRegistry = String.Format(jPattern, Microsoft.Win32.Registry.ClassesRoot.Name, Root, vbNewLine)
            jRegistry = jRegistry & String.Format(jPattern, Microsoft.Win32.Registry.CurrentUser.Name, Root, vbNewLine)
            jRegistry = jRegistry & String.Format(jPattern, Microsoft.Win32.Registry.LocalMachine.Name, Root, vbNewLine)
            jRegistry = jRegistry & String.Format(jPattern, Microsoft.Win32.Registry.Users.Name, Root, vbNewLine)
        Else
            Using RootKey = GetRootKey(Root)
                For Each K In RootKey.GetSubKeyNames
                    jRegistry = jRegistry & String.Format(jPattern, K, Root, vbNewLine)
                Next
            End Using
        End If
        Return String.Format("[{0}{1}{0}]", vbNewLine, jRegistry.Trim(","))
    End Function

    Public Function NewKey(ByVal Root As String, ByVal KeyName As String) As String
        Dim objGeneric As New GenericJson
        Try
            Using RootKey = GetRootKey(Root, True)
                RootKey.CreateSubKey(KeyName)
            End Using
            objGeneric.Success = True
        Catch ex As Exception
            objGeneric.Success = False
            objGeneric.Msg = ex.Message
        End Try
        Return objGeneric.ToString
    End Function

    Public Function NewValue(ByVal Root As String, ByVal ValueName As String, ByVal ValueData As String, ByVal Kind As String, ByVal IsHex As Boolean) As String
        Dim objGeneric As New GenericJson
        Dim ObjValue As Object
        Dim RegKind As Microsoft.Win32.RegistryValueKind
        Try
            Select Case Kind
                Case "dword"
                    If IsHex Then
                        ObjValue = Conversion.Val("&H" & ValueData)
                    Else
                        ObjValue = ValueData
                    End If
                    RegKind = Microsoft.Win32.RegistryValueKind.DWord
                Case "binary"
                    Dim LBytes = ValueData.Split("-").Select(Function(p) CByte(Conversion.Val("&H" & p))).ToList
                    ObjValue = LBytes.ToArray
                    RegKind = Microsoft.Win32.RegistryValueKind.Binary
                Case "string"
                    RegKind = Microsoft.Win32.RegistryValueKind.String
                    ObjValue = ValueData
                Case "multistring"
                    RegKind = Microsoft.Win32.RegistryValueKind.MultiString
                    ObjValue = ValueData.Split("%0A".ToCharArray).Where(Function(p) Not String.IsNullOrEmpty(p)).ToArray
                Case "expandablestring"
                    RegKind = Microsoft.Win32.RegistryValueKind.ExpandString
                    ObjValue = ValueData
                Case Else
                    RegKind = Microsoft.Win32.RegistryValueKind.String
                    ObjValue = ValueData
            End Select
            Using RootKey = GetRootKey(Root, True)
                RootKey.SetValue(ValueName, ObjValue, RegKind)
            End Using
            objGeneric.Success = True
        Catch ex As Exception
            objGeneric.Success = False
            objGeneric.Msg = ex.Message
        End Try
        Return objGeneric.ToString
    End Function

    Public Function DeleteRegistry(ByVal Root As String, ByVal Data As String, ByVal Kind As String) As String
        Dim RootKey = GetRootKey(Root, True)
        If Kind = "key" Then
            RootKey.DeleteSubKeyTree(Data)
        Else
            RootKey.DeleteValue(Data)
        End If
        Return (New GenericJson).ToString
    End Function

    Public Function GetSubKeysValues(ByVal Root As String, ByVal Start As Integer, ByVal Limit As Integer)
        Dim ListKeysRows As New KeysValues

        If Root = "Registry" Then
            ListKeysRows.Rows = New List(Of KeyValue)
            ListKeysRows.Rows.Add(New KeyValue With {.Name = Microsoft.Win32.Registry.ClassesRoot.Name, .Data = "", .Type = "Key"})
            ListKeysRows.Rows.Add(New KeyValue With {.Name = Microsoft.Win32.Registry.CurrentUser.Name, .Data = "", .Type = "Key"})
            ListKeysRows.Rows.Add(New KeyValue With {.Name = Microsoft.Win32.Registry.LocalMachine.Name, .Data = "", .Type = "Key"})
            ListKeysRows.Rows.Add(New KeyValue With {.Name = Microsoft.Win32.Registry.Users.Name, .Data = "", .Type = "Key"})
        Else
            Using RootKey = GetRootKey(Root)
                Dim ListAll = (From u In RootKey.GetSubKeyNames _
                               Select New KeyValue With {.Data = "", .Name = u, .Type = "Key"}).ToList

                Dim ListValues = (From u In RootKey.GetValueNames _
                                  Select New KeyValue With {.Name = u, .Data = GetFormatedValue(RootKey.GetValue(u), RootKey.GetValueKind(u)), .Type = RootKey.GetValueKind(u).ToString, .EditValue = GetUnFormatedValue(RootKey.GetValue(u), RootKey.GetValueKind(u))})

                ListAll = ListAll.Concat(ListValues).ToList
                ListKeysRows.Results = ListAll.Count
                ListKeysRows.Rows = ListAll.Skip(Start).Take(Limit).ToList
            End Using
        End If

        Return ToJson(ListKeysRows)
    End Function

    Private Function GetRootKey(ByVal Root As String, Optional ByVal Writable As Boolean = False) As Microsoft.Win32.RegistryKey
        Root = Root.URLDecode
        Dim PhysicalPath = Root.Substring("Registry/".Length)
        Dim RootKey As Microsoft.Win32.RegistryKey
        Dim HKey As String
        If PhysicalPath.IndexOf("/") > 0 Then
            HKey = PhysicalPath.Substring(0, PhysicalPath.IndexOf("/"))
        Else
            HKey = PhysicalPath
        End If

        Select Case HKey
            Case Microsoft.Win32.Registry.ClassesRoot.Name
                RootKey = Microsoft.Win32.Registry.ClassesRoot
            Case Microsoft.Win32.Registry.CurrentUser.Name
                RootKey = Microsoft.Win32.Registry.CurrentUser
            Case Microsoft.Win32.Registry.LocalMachine.Name
                RootKey = Microsoft.Win32.Registry.LocalMachine
            Case Else
                RootKey = Microsoft.Win32.Registry.Users
        End Select
        If PhysicalPath.IndexOf("/") > 0 Then
            Dim SubKeyName = PhysicalPath.Substring(PhysicalPath.IndexOf("/") + 1).Replace("/", "\")
            RootKey = RootKey.OpenSubKey(SubKeyName, Writable)
        End If
        Return RootKey
    End Function

    Private Function GetFormatedValue(ByVal Value As Object, ByVal ValueKind As Microsoft.Win32.RegistryValueKind) As String
        Select Case ValueKind
            Case Microsoft.Win32.RegistryValueKind.MultiString
                Dim S() As String = Value
                Return S.ToList.Aggregate(Function(s1, s2) s1 & " " & s2)
            Case Microsoft.Win32.RegistryValueKind.Binary
                Dim B() As Byte = Value
                Return B.ToList.Cast(Of String).ToList.Aggregate(Function(b1, b2) b1 & "-" & Conversion.Hex(b2))
            Case Microsoft.Win32.RegistryValueKind.DWord
                Return String.Format("0x{0} ({1})", Conversion.Hex(Value), Value)
            Case Else
                Return Value.ToString
        End Select
    End Function

    Private Function GetUnFormatedValue(ByVal Value As Object, ByVal ValueKind As Microsoft.Win32.RegistryValueKind) As String
        Select Case ValueKind
            Case Microsoft.Win32.RegistryValueKind.MultiString
                Dim S() As String = Value
                Return S.ToList.Aggregate(Function(s1, s2) s1 & "\n" & s2)
            Case Microsoft.Win32.RegistryValueKind.Binary
                Dim B() As Byte = Value
                Return B.ToList.Cast(Of String).ToList.Aggregate(Function(b1, b2) b1 & "-" & Conversion.Hex(b2))
            Case Microsoft.Win32.RegistryValueKind.DWord
                Return String.Format("{0}", Conversion.Hex(Value))
            Case Else
                Return Value.ToString
        End Select
    End Function

    Private Class KeyValue

        Private _Name As String
        Public Property Name() As String
            Get
                Return _Name
            End Get
            Set(ByVal value As String)
                _Name = value
            End Set
        End Property

        Private _Type As String
        Public Property Type() As String
            Get
                Return _Type
            End Get
            Set(ByVal value As String)
                _Type = value
            End Set
        End Property

        Private _Data As String
        Public Property Data() As String
            Get
                Return _Data
            End Get
            Set(ByVal value As String)
                _Data = value
            End Set
        End Property

        Private _EditValue As String
        Public Property EditValue() As String
            Get
                Return _EditValue
            End Get
            Set(ByVal value As String)
                _EditValue = value
            End Set
        End Property

    End Class

    Private Class KeysValues

        Private _Results As Integer
        Public Property Results() As Integer
            Get
                Return _Results
            End Get
            Set(ByVal value As Integer)
                _Results = value
            End Set
        End Property

        Private _Success As Boolean = True
        Public Property Success() As Boolean
            Get
                Return _Success
            End Get
            Set(ByVal value As Boolean)
                _Success = value
            End Set
        End Property

        Private _Rows As List(Of KeyValue)
        Public Property Rows() As List(Of KeyValue)
            Get
                Return _Rows
            End Get
            Set(ByVal value As List(Of KeyValue))
                _Rows = value
            End Set
        End Property
    End Class

End Class
